<?php
/**
 * HTTP服务 - 控制器基类
*/

namespace houze\controller;

class Http {
    /**
     * 表单
    */
    private $file;
    /**
     * 文件上传配置
    */
    private $file_config=[];
    /**
     * 文件上传错误提示
    */
    private $file_error;
    /**
     * 文件保存名称
    */
    private $file_name;
    /**
     * 文件保存地址
    */
    private $file_path;
    /**
     * 视图实例
     */
    private $view;
    /**
     * 模板变量
    */
    private $assign = [];

    /**
     * 利用析构函数，自动回收归还连接池
    */
    public function __destruct() {
        $list = get_object_vars($this);
        foreach ($list as $name=>$value) {
            if (is_object($value)) {
                $obj = get_class($value);
                if ($obj == 'houze\\Db' || $obj == 'houze\\Redis') {
                    $this->$name->return();
                }
            }
        }
    }

    /**
     * 输出内容到页面
    */
    public final function fetch($string, $status=200, $headers=[]) {
        // 清空模板变量，防止大意的开发者
        $this->assign = [];
        if (!empty($this->view)) $this->view->delete_assign();

        $Response = \houze\context\Response::get();
        if (!$Response) return false;

        if (empty($headers)) {
            $headers = \houze\Config::get('view.http_response_headers');
        }
        
        if (!empty($headers)){
            $Response->send(new \Workerman\Protocols\Http\Response(200, $headers, $string));
            $Response->close();
        }else{
            $status = $Response->send($string);
            $Response->close();
        }
    }

    /**
     * 模板变量赋值
    */
    public final function assign($name, $value = '')
    {
        $this->assign[$name] = $value;
        return $this;
    }

    /**
     * 加载模板输出
    */
    public final function view($template = '', $vars = [], $config = [])
    {
        $this->is_view();
        foreach ($this->assign as $name=>$value) {
            $this->view->assign($name, $value);
        }
        $content = $this->view->fetch($template, $vars, $config);
        return $content;
    }

    /**
     * 渲染内容输出
    */
    public final function display($content = '', $vars = [], $config = [])
    {
        $this->is_view();
        foreach ($this->assign as $name=>$value) {
            $this->view->assign($name, $value);
        }
        $content = $this->view->display($content, $vars, $config);
        // 清空模板变量，防止大意的开发者
        $Response = \houze\context\Response::get();
        
        // 清空模板变量，防止大意的开发者
        unset($vars);
        $this->assign = [];
        if (!empty($this->view)) $this->view->delete_assign();

        $DebugGer = new \houze\DebugGer();
        $debug_html = $DebugGer->run();
        $Response->send($content.$debug_html);
        $Response->close();
    }

    /**
     * 视图过滤
    */
    public final function filter($filter)
    {
        $this->is_view();
        $this->view->filter($filter);
        return $this;
    }

    /**
     * 模板布局开关
    */
    public final function layout($mixed) {
        $this->is_view();
        $this->view->engine->layout($mixed);
        return $this;
    }

    /**
     * 重定向
    */
    public final function redirect($url, $status=302, $data=[]) {
        $url = $this->get_url($url, $data);
        $Response = \houze\context\Response::get();
        return $Response->send(new \Workerman\Protocols\Http\Response($status, ['Location' => $url]));
    }

    /**
     * 文件上传1-注入表单
    */
    public final function file($name=null) {
        $this->file = $name;
        return $this;
    }

    /**
     * 文件上传2-注入配置
    */
    public final function validate($config=[]) {
        $this->file_config = $config;
        return $this;
    }

    /**
     * 文件上传3-保存
    */
    public final function move($path=null, $name=null) {
        $config = \houze\Config::get('app.file');
        $config = array_merge($config, $this->file_config);

        if ($path) {
            $config['path'] = $path;
        }

        $file_data = $this->file;
        if (is_array($this->file) == false) {
            $Request = \houze\context\Request::get();
            if ($Request->file($this->file) == false) {
                $this->file_error = 'Form does not exist';
                return false;
            }
            $file_data = $Request->file($this->file);
        }

        if ($file_data['size'] > $config['size']) {
            $this->file_error = 'Upload exceeds maximum limit';
            return false;
        }

        $suffix_array = explode(',', str_replace(['.', ' '], '', $config['ext']));
        $arr = explode('.', $file_data['name']);
        $suffix = end($arr);
        
        if (in_array($suffix, $suffix_array) == false) {
            $this->file_error = 'Upload file type error';
            return false;
        }

        if (file_exists($config['path']) == false) {
            if ($config['auto_save'] == false) {
                $this->file_error = 'Save directory does not exist';
                return false;
            }

            $res = mkdir($config['path'], 0755, true); 
            if ($res === false) {
                $this->file_error = 'Save directory auto create failed';
                return false;
            }
        }
        
        $cutting = substr($config['path'], strlen($config['path'])-1, 1);
        if ($cutting != '/' && $cutting != '\\') {
            $config['path'] .= '/';
        }
        $config['path'] .= date('Ymd', time()).'/';

        if (file_exists($config['path']) == false) {
            if ($config['auto_save'] == false) {
                $this->file_error = 'Save directory does not exist';
                return false;
            }

            $res = mkdir($config['path'], 0755, true); 
            if ($res === false) {
                $this->file_error = 'Save directory auto create failed';
                return false;
            }
        }

        if (!$name) {
            if ($config['name_algorithm'] == 'time') {
                $name = time().'_'.rand();
            } else if ($config['name_algorithm'] == 'sha1') {
                $name = sha1(time().rand());
            } else if ($config['name_algorithm'] == 'md5') {
                $name = md5(time().rand());
            } else if ($config['name_algorithm'] == 'none') {
                $name = $file_data['name'];
            } else {
                $this->file_error = 'Unsupported filename generation';
                return false;
            }
            if($config['name_algorithm'] != 'none') $name .= '.'.$suffix;
        }

        $res = move_uploaded_file($file_data['tmp_name'], $config['path'].$name);
        if (!$res) {
            $this->file_error = 'File save failed';
            return false;
        }

        $this->file_name = $name;
        $this->file_path = $config['path'].$name;
        return $this;
    }

    /**
     * 文件上传4-获取错误日志
    */
    public final function getError() {
        return $this->file_error;
    }

    /**
     * 文件上传4-获取上传的文件名称
    */
    public final function getFileName() {
        return $this->file_name;
    }

    /**
     * 文件上传4-获取上传的文件完整路径
    */
    public final function getSaveName() {
        return $this->file_path;
    }

    /**
     * 构造出跳转地址
    */
    private function get_url($url, $data) {
        if (strpos($url, '//') === false) {
            $url = '/'.ltrim($url, '/');
            if ($url != '/') {
                $url .= \houze\Config::get('route.suffix');
            }
            $url = \houze\Request::domain().$url;
        }
        if (count($data)) {
            $url .= '?'.http_build_query($data);
        }
        return $url;
    }

    /**
     * 获得模板实例 
    */
    private function is_view(){
        if (empty($this->view)) {
            $this->view = new \houze\View(\houze\Config::get('view'));
        }
    }
    
    /**
     * 输出接口数据到页面
    */
    public final function api($code=1, $msg='ok', $data=[], $extend=[], $headers=['Content-Type' => 'application/json'], $status=200) {
        // 清空模板变量，防止大意的开发者
        $this->assign = [];
        if (!empty($this->view)) $this->view->delete_assign();

        $Response = \houze\context\Response::get();
        if (!$Response) return false;
       
        // 获取配置
        $config = \houze\Config::get('restful');
        // 结果数组
        $result = [
            'code'=>$code,
            'msg'=>$msg,
            'data'=>$data,
            'time'=>time()
        ];
        // 扩展数组
        foreach($extend as $k => $v){
            $result[$k] = $v;
        }
        if($config['type'] == 'xml'){
            // 生成Xml字符串
            $xml = $this->xmlToEncode($result);
            $result  ="<?xml version='1.0' encoding='UTF-8'?>";
            $result .= "<root>";
            $result .= $xml;
            $result .= "</root>";
        }else{
            // 生成Json字符串
            $result = json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        
        // 判断是否加密数据
        if($config['aes_status']) $result = \houze\Currency::set('restful', $result);
        
        // 输出结果
        if (!empty($headers)){
            $Response->send(new \Workerman\Protocols\Http\Response(200, $headers, $result));
            $Response->close();
        }else{
            $status = $Response->send($result);
            $Response->close();
        }
    }
    
    /**
     * 数组转XML
    */
    public function xmlToEncode($data){
        $xml = $attr = '';
        foreach ($data as $key=>$value) {
            if(is_numeric($key)){
                $attr = "id='{$key}'";
                $key = "item";
            }
            $xml .= "<{$key} {$attr}>";
            $xml .= is_array($value) ? $this->xmlToEncode($value) : $value;
            $xml .= "</{$key}>";
        }
        return $xml;
    }
    
    /**
     * 生成静态页面与重定向
    */
    public final function push($content = '', $dir = '', $filename = '', $cache = true, $redirect = true, $vars = [], $config = [])
    {
        // 静态文件路径
        $static_path = ROOT_PATH.\houze\Config::get('view.static_path');
        $root_dir = $static_path.DS.$dir;
        $path = $root_dir.DS.$filename;
        
        // 是否开启缓存模式和重定向模式
        if($cache && $redirect){
            if(is_file($path)){
                $url = \houze\Request::domain().DS.$dir.DS.$filename;
                $Response = \houze\context\Response::get();
                return $Response->send(new \Workerman\Protocols\Http\Response(302, ['Location' => $url]));
            }
        }
        
        $this->is_view();
        foreach ($this->assign as $name=>$value) {
            $this->view->assign($name, $value);
        }
        $content = $this->view->display($content, $vars, $config);
        // 清空模板变量，防止大意的开发者
        $Response = \houze\context\Response::get();
        
        // 清空模板变量，防止大意的开发者
        unset($vars);
        $this->assign = [];
        if (!empty($this->view)) $this->view->delete_assign();
        
        // 生成静态文件目录
        if(!is_dir($root_dir)) mkdir($root_dir, 0755, true);
        // 生成静态文件
        if(\houze\File::set($path, $content)){
            $url = \houze\Request::domain().DS.$dir.DS.$filename;
            if($redirect){
                $Response = \houze\context\Response::get();
                return $Response->send(new \Workerman\Protocols\Http\Response(302, ['Location' => $url]));
            }else{
                return $url;
            } 
        }
        
        return false;
    }
    
    /**
     * 成功跳转提示    
     */
    public final function success($msg = '成功', $url = null){
        if(empty($url)) $url = \houze\context\Request::get()->path();
        $exceptionFile = TEMPLATE_PATH.'tpl/jump.php';
        // 引入文件
        ob_start();
        include $exceptionFile;
        $html = ob_get_clean();

        $this->fetch($html);
    }
    
    /**
     * 失败跳转提示    
     */
    public final function error($msg = '失败', $url = null){
        if(empty($url)) $url = \houze\context\Request::get()->path();
        $exceptionFile = TEMPLATE_PATH.'tpl/jump.php';
        // 引入文件
        ob_start();
        include $exceptionFile;
        $html = ob_get_clean();

        $this->fetch($html);
    }
    
    /**
     * 图形验证码
	*/
	public final function captcha($num=1, $session=null, $type=null) {
        $Response = \houze\context\Response::get();
        \houze\captcha\Figure::entry($Response, $num, $session, $type);
    }

    /**
     * 图形验证码校验
	*/
	public final function captcha_check($code, $session=null) {
        return \houze\captcha\Figure::check($code, $session);
    }
}